/*
 * Copyright 2020-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.xunlai.infra.permission.manager.authority.config;

import com.xunlai.infra.permission.common.util.SM2Utils;
import com.xunlai.infra.permission.manager.authority.login.*;
import com.xunlai.infra.permission.manager.authority.sercurity.FederatedIdentityConfigurer;
import com.xunlai.infra.permission.manager.authority.sercurity.UserRepositoryOAuth2UserHandler;
import com.xunlai.infra.permission.manager.authority.sercurity.passowrd.SM2PasswordEncoder;
import com.xunlai.infra.permission.manager.authority.sercurity.user.DelegateUserDetailsManager;
import com.xunlai.infra.permission.manager.authority.web.CaptchaStorage;
import com.xunlai.infra.permission.manager.authority.web.SimpleCaptchaStorage;
import com.xunlai.infra.permission.storage.repository.AuthGroupCrewRepository;
import com.xunlai.infra.permission.storage.repository.RoleRelationRepository;
import com.xunlai.infra.permission.storage.repository.UserAccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.time.Duration;
import java.util.Collections;

/**
 * @author Steve Riesenberg
 * @since 0.2.3
 */
@EnableMethodSecurity
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class DefaultSecurityConfig {

	@Autowired
	@Qualifier("simpleCaptchaStorage")
	private CaptchaStorage captchaStorage;

	@Autowired
	private UserDetailsService userDetailsService;

	@Autowired
	private PasswordEncoder passwordEncoder;
	// @formatter:off
	@Bean
	public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
		FederatedIdentityConfigurer federatedIdentityConfigurer = new FederatedIdentityConfigurer()
			.oauth2UserHandler(new UserRepositoryOAuth2UserHandler());
		HttpSessionSecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository();
		CaptchaAuthorizationProvider authenticationProvider = new CaptchaAuthorizationProvider(captchaStorage);
		authenticationProvider.setUserDetailsService(userDetailsService);
		authenticationProvider.setPasswordEncoder(passwordEncoder);
		http
				.csrf().disable()
			.authorizeHttpRequests(authorize ->
				authorize
					.requestMatchers(permitRequestUris()).permitAll()
					.anyRequest().authenticated()
			)
				.securityContext(s -> {
					s.securityContextRepository(securityContextRepository);
//					s.
				})
				.authenticationProvider(authenticationProvider)
			.formLogin(form -> form.failureHandler(failureHandler()))
				.logout(logout-> {
					logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
					logout.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler());
				})
				.exceptionHandling(exception -> {
					exception.authenticationEntryPoint(new LoginBodyAuthenticationEntryPoint(""));
				})
//			.apply(federatedIdentityConfigurer)
				.cors(c -> c.configurationSource(corsConfigurationSource()))
		;
		JsonUsernamePasswordAuthenticationFilter filter = jsonUsernamePasswordAuthenticationFilter();
		http.addFilterAt(filter, UsernamePasswordAuthenticationFilter.class);
		DefaultSecurityFilterChain filterChain = http.build();
		AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
		filter.setAuthenticationManager(authenticationManager);
		filter.setAuthenticationSuccessHandler(successHandler());
		filter.setAuthenticationFailureHandler(failureHandler());
		filter.setSecurityContextRepository(securityContextRepository);
		return filterChain;
	}

	private String[] permitRequestUris(){
		return new String[]{
				"/assets/**",
				"/webjars/**",
				"login",
				"/login/**",
				"logout",
				"/travel/manage/order/wxpay/notify",
				"/travel/manage/route/transfer",
				"/travel/manage/route/detail/**",
				"/travel/manage/route/type/all",
				"/travel/manage/route/subject/all",
				"/travel/manage/poster/create",
				"/travel/manage/ad/picture/active/all",
				"/travel/manage/route/mylist",
				"/travel/manage/route/view/*",
				"/travel/manage/route/batch/transfer"
		};
	}
	// @formatter:on

	// @formatter:off

//	@Bean
//	public UserDetailsService users() {
//		UserDetails user = User.withDefaultPasswordEncoder()
//				.username("user1")
//				.password("password")
//				.roles("USER")
//				.build();
//		return new InMemoryUserDetailsManager(user);
//	}

//	@Bean
	public JsonUsernamePasswordAuthenticationFilter jsonUsernamePasswordAuthenticationFilter(){
		return new JsonUsernamePasswordAuthenticationFilter();
	}

	public JwtTokenResponseBodyAuthenticationSuccessHandler successHandler (){
		return new JwtTokenResponseBodyAuthenticationSuccessHandler();
	}

	public TwoFactorAuthenticationFailureHandler failureHandler(){
		return new TwoFactorAuthenticationFailureHandler();
	}


	public CorsConfigurationSource corsConfigurationSource() {
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowCredentials(true);
//		configuration.setAllowedOrigins(Collections.singletonList("*"));
		configuration.setAllowedOriginPatterns(Collections.singletonList("*"));
		configuration.setAllowedMethods(Collections.singletonList("*"));
		configuration.setAllowedHeaders(Collections.singletonList("*"));
		configuration.setMaxAge(Duration.ofHours(1));
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}

//	@Bean
//	public CaptchaStorage captchaStorage(){
//		return new SimpleCaptchaStorage();
//	}
}
